package edu.northwestern.cbits.purple_robot_manager.scripting;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import edu.northwestern.cbits.purple_robot_manager.R;
import edu.northwestern.cbits.purple_robot_manager.annotation.ScriptingEngineMethod;
import edu.northwestern.cbits.purple_robot_manager.models.ModelManager;
import jscheme.JScheme;
import jscheme.SchemeException;
import jsint.Evaluator;
import jsint.Pair;
import jsint.Symbol;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import edu.northwestern.cbits.purple_robot_manager.ManagerService;
import edu.northwestern.cbits.purple_robot_manager.ScheduleManager;
import edu.northwestern.cbits.purple_robot_manager.config.SchemeConfigFile;
import edu.northwestern.cbits.purple_robot_manager.logging.LogManager;
import edu.northwestern.cbits.purple_robot_manager.probes.features.Feature;
public class SchemeEngine extends BaseScriptEngine
{
public SchemeEngine(Context context, Map<String, Object> objects)
{
super(context);
}
@SuppressLint("DefaultLocale")
public static boolean canRun(String script)
{
script = script.trim();
if (script == null || script.length() < 1)
return false;
else if (script.charAt(0) == '(')
{
if (script.charAt(script.length() - 1) == ';')
return false;
else
{
JScheme scheme = new JScheme();
Object pair = scheme.read(script);
if ((pair instanceof Pair) == false)
return false;
}
return true;
}
return false;
}
@SuppressLint("DefaultLocale")
@ScriptingEngineMethod(language = "Scheme")
public Object evaluateSource(String source)
{
if (source.trim().toLowerCase().equals("(begin)"))
return null;
Evaluator eval = new Evaluator();
eval.getInteractionEnvironment().setValue(Symbol.intern("PurpleRobot"), this);
eval.getInteractionEnvironment().setValue(Symbol.intern("JSONHelper"), new JSONHelper());
JScheme scheme = new JScheme(eval);
try
{
scheme.load(new InputStreamReader(this._context.getAssets().open("scheme/pregexp.scm")));
}
catch (IOException e)
{
LogManager.getInstance(this._context).logException(e);
}
catch (StackOverflowError e)
{
LogManager.getInstance(this._context).logException(e);
}
try
{
scheme.load(new InputStreamReader(this._context.getAssets().open("scheme/json.scm")));
}
catch (IOException e)
{
LogManager.getInstance(this._context).logException(e);
}
catch (StackOverflowError e)
{
LogManager.getInstance(this._context).logException(e);
}
try
{
scheme.load(new InputStreamReader(this._context.getAssets().open("scheme/purple-robot.scm")));
}
catch (IOException e)
{
LogManager.getInstance(this._context).logException(e);
}
catch (StackOverflowError e)
{
LogManager.getInstance(this._context).logException(e);
}
try
{
return scheme.eval(source);
}
catch (StackOverflowError e)
{
LogManager.getInstance(this._context).logException(e);
}
return false;
}
protected String language()
{
return "Scheme";
}
@ScriptingEngineMethod(language = "All", assetPath = "all_update_trigger.html", category = R.string.docs_script_category_triggers_automation, arguments = { "identifier", "definition" })
public boolean updateTrigger(String triggerId, Pair parameters)
{
Map<String, Object> paramsMap = SchemeEngine.parsePairList(parameters);
paramsMap.put("identifier", triggerId);
return super.updateTrigger(triggerId, paramsMap);
}
@ScriptingEngineMethod(language = "All", assetPath = "all_update_trigger.html", category = R.string.docs_script_category_triggers_automation, arguments = { "identifier", "definition" })
public boolean updateTrigger(Pair parameters)
{
Map<String, Object> paramsMap = SchemeEngine.parsePairList(parameters);
if (paramsMap.containsKey("identifier"))
{
String triggerId = paramsMap.get("identifier").toString();
return this.updateTrigger(triggerId, parameters);
}
return false;
}
@ScriptingEngineMethod(language = "All", assetPath = "all_schedule_script.html", category = R.string.docs_script_category_dialogs_notifications, arguments = { "identifier", "dateString", "script" })
public void scheduleScript(String identifier, String dateString, Pair action)
{
super.scheduleScript(identifier, dateString, action.toString());
}
@ScriptingEngineMethod(language = "All", assetPath = "all_update_probe.html", category = R.string.docs_script_category_data_collection, arguments = { "parameters" })
public boolean updateProbe(Pair params)
{
Map<String, Object> map = SchemeEngine.parsePairList(params);
return this.updateProbeConfig(map);
}
public static Map<String, Object> parsePairList(Pair pair)
{
HashMap<String, Object> map = new HashMap<>();
if (pair.isEmpty() == false)
{
Object first = pair.getFirst();
if (first instanceof Pair)
{
Pair firstPair = (Pair) first;
String key = firstPair.first.toString();
Object value = firstPair.rest();
if (value instanceof Symbol)
{
Symbol symbol = (Symbol) value;
value = symbol.getGlobalValue();
}
if (value instanceof Pair)
{
Pair valuePair = (Pair) value;
// value = valuePair.toString();
if (valuePair.first() instanceof String && valuePair.rest() instanceof Pair)
{
ArrayList<Object> list = new ArrayList<>();
list.add(valuePair.first());
Pair restPair = (Pair) valuePair.rest();
while (restPair.isEmpty() == false)
{
list.add(restPair.first());
if (restPair.rest() instanceof Pair)
restPair = (Pair) restPair.rest();
else
restPair = Pair.EMPTY;
}
value = list;
}
else
value = SchemeEngine.parsePairList(valuePair);
}
map.put(key, value);
}
Object rest = pair.getRest();
if (rest instanceof Pair)
{
Pair restPair = (Pair) rest;
Map<String, Object> restMap = SchemeEngine.parsePairList(restPair);
map.putAll(restMap);
}
}
return map;
}
private static Bundle bundleForPair(Pair pair)
{
Bundle b = new Bundle();
if (pair.isEmpty() == false)
{
Object first = pair.first();
if (first instanceof Pair)
{
Pair firstPair = (Pair) first;
Object firstFirst = firstPair.first();
if (firstFirst instanceof String)
{
String key = (String) firstFirst;
Object second = firstPair.rest();
if (second instanceof String)
b.putString(key, second.toString());
else if (second instanceof Pair)
{
Pair secondPair = (Pair) second;
if (secondPair.first().toString().equalsIgnoreCase("begin"))
b.putString(key, secondPair.toString());
else
b.putBundle(key, SchemeEngine.bundleForPair(secondPair));
}
}
}
Object rest = pair.rest();
if (rest instanceof Pair && !((Pair) rest).isEmpty())
{
Pair restPair = (Pair) rest;
Bundle restBundle = SchemeEngine.bundleForPair(restPair);
b.putAll(restBundle);
}
}
return b;
}
@ScriptingEngineMethod(language = "All", assetPath = "all_show_native_dialog.html", category = R.string.docs_script_category_dialogs_notifications, arguments = { "title", "message", "confirmLabel", "cancelLabel", "confirmScript", "cancelScript", "tag", "priority" })
public void showNativeDialog(String title, String message, String confirmTitle, String cancelTitle,
Pair confirmAction, Pair cancelAction)
{
this.showNativeDialog(title, message, confirmTitle, cancelTitle, confirmAction.toString(),
cancelAction.toString());
}
@ScriptingEngineMethod(language = "All", assetPath = "all_show_native_dialog.html", category = R.string.docs_script_category_dialogs_notifications, arguments = { "title", "message", "confirmLabel", "cancelLabel", "confirmScript", "cancelScript", "tag", "priority" })
public void showNativeDialog(String title, String message, String confirmTitle, String cancelTitle,
Pair confirmAction, Pair cancelAction, String tag, int priority)
{
this.showNativeDialog(title, message, confirmTitle, cancelTitle, confirmAction.toString(),
cancelAction.toString(), tag, priority);
}
@ScriptingEngineMethod(language = "All", assetPath = "all_update_config_map.html", category = R.string.docs_script_category_configuration, arguments = { "configMap" })
public boolean updateConfig(Pair parameters)
{
Map<String, Object> paramsMap = SchemeEngine.parsePairList(parameters);
return super.updateConfig(paramsMap);
}
@ScriptingEngineMethod(language = "Scheme")
public void updateWidget(Pair parameters)
{
Map<String, Object> paramsMap = SchemeEngine.parsePairList(parameters);
Intent intent = new Intent(ManagerService.UPDATE_WIDGETS);
intent.setClass(this._context, ManagerService.class);
for (String key : paramsMap.keySet())
{
intent.putExtra(key, paramsMap.get(key).toString());
}
this._context.startService(intent);
}
@ScriptingEngineMethod(language = "All", assetPath = "all_readings.html", category = R.string.docs_script_category_data_collection, arguments = { })
public Pair readings()
{
Map<String, Object> readings = ModelManager.getInstance(this._context).readings(this._context);
return this.pairForMap(readings);
}
@ScriptingEngineMethod(language = "All", assetPath = "all_emit_reading.html", category = R.string.docs_script_category_data_collection, arguments = { "name", "value", "priority" })
public void emitReading(String name, Object value)
{
this.emitReading(name, value, false);
}
@ScriptingEngineMethod(language = "All", assetPath = "all_emit_reading.html", category = R.string.docs_script_category_data_collection, arguments = { "name", "value", "priority" })
public void emitReading(String name, Object value, boolean priority)
{
Bundle bundle = new Bundle();
bundle.putString("PROBE", name);
bundle.putLong("TIMESTAMP", System.currentTimeMillis() / 1000);
if (priority)
bundle.putBoolean("PRIORITY", true);
if (value instanceof String)
bundle.putString(Feature.FEATURE_VALUE, value.toString());
else if (value instanceof Double)
{
Double d = (Double) value;
bundle.putDouble(Feature.FEATURE_VALUE, d);
}
else if (value instanceof Integer)
{
Integer i = (Integer) value;
bundle.putDouble(Feature.FEATURE_VALUE, i.doubleValue());
}
else if (value instanceof Pair)
{
Pair pair = (Pair) value;
try
{
Bundle b = SchemeEngine.bundleForPair(pair);
bundle.putParcelable(Feature.FEATURE_VALUE, b);
}
catch (SchemeException e)
{
e.printStackTrace();
}
}
else
{
Log.e("PR", "SCHEME ENGINE GOT UNKNOWN VALUE " + value);
if (value != null)
Log.e("PR", "SCHEME ENGINE GOT UNKNOWN CLASS " + value.getClass());
}
this.transmitData(bundle);
}
@ScriptingEngineMethod(language = "All", assetPath = "all_broadcast_intent.html", category = R.string.docs_script_category_system_integration, arguments = { "action", "extras" })
public boolean broadcastIntent(final String action, Pair extras)
{
return this.broadcastIntent(action, SchemeEngine.parsePairList(extras));
}
@ScriptingEngineMethod(language = "Scheme")
public boolean updateWidget(final String title, final String message, final String applicationName,
final Pair launchParams, final String script)
{
return this.updateWidget(title, message, applicationName, SchemeEngine.parsePairList(launchParams), script);
}
@ScriptingEngineMethod(language = "All", assetPath = "all_fetch_labels.html", category = R.string.docs_script_category_data_collection, arguments = { "instructions", "labels" })
public void fetchLabels(String appContext, String instructions, final Pair labels)
{
super.fetchLabelsInterface(appContext, instructions, SchemeEngine.parsePairList(labels));
}
@ScriptingEngineMethod(language = "All", assetPath = "all_launch_application.html", category = R.string.docs_script_category_system_integration, arguments = { "applicationName", "options", "script" })
public boolean launchApplication(String applicationName, final Pair launchParams, final String script)
{
return this.launchApplication(applicationName, SchemeEngine.parsePairList(launchParams), script);
}
@ScriptingEngineMethod(language = "All", assetPath = "all_show_app_launch_note.html", category = R.string.docs_script_category_dialogs_notifications, arguments = { "title", "message", "packageName" })
public boolean showApplicationLaunchNotification(String title, String message, String applicationName,
boolean persistent, final Pair launchParams, final String script)
{
return this.showApplicationLaunchNotification(title, message, applicationName, persistent,
SchemeEngine.parsePairList(launchParams), script);
}
@ScriptingEngineMethod(language = "All", assetPath = "all_show_app_launch_note.html", category = R.string.docs_script_category_dialogs_notifications, arguments = { "title", "message", "packageName" })
public boolean showApplicationLaunchNotification(String title, String message, String applicationName,
final Pair launchParams, final String script)
{
return this.showApplicationLaunchNotification(title, message, applicationName,
SchemeEngine.parsePairList(launchParams), script);
}
@ScriptingEngineMethod(language = "All", assetPath = "all_fetch_config.html", category = R.string.docs_script_category_configuration, arguments = { })
public String fetchConfig()
{
SchemeConfigFile config = new SchemeConfigFile(this._context);
return config.toString();
}
public Pair dateToComponents(Date date)
{
String stringRep = this.formatDate(date);
String year = stringRep.substring(0, 4);
String month = stringRep.substring(4, 6);
String day = stringRep.substring(6, 8);
String hour = stringRep.substring(9, 11);
String minute = stringRep.substring(11, 13);
String second = stringRep.substring(13, 15);
return new Pair(year, new Pair(month, new Pair(day, new Pair(hour, new Pair(minute, new Pair(second, null))))));
}
public Date dateFromComponents(Pair components)
{
String year = components.nth(0).toString();
String month = components.nth(1).toString();
String day = components.nth(2).toString();
String hour = components.nth(3).toString();
String minute = components.nth(4).toString();
String second = components.nth(5).toString();
String dateString = year + month + day + "T" + hour + minute + second;
return ScheduleManager.parseString(dateString);
}
@ScriptingEngineMethod(language = "Scheme", assetPath = "scheme_nth.html", category = R.string.docs_script_category_language_enhancements, arguments = { "index", "list" })
public Object nth(int index, Object obj)
{
if (obj == null)
return null;
else if (index < 0)
return null;
else if ((obj instanceof Pair) == false)
return null;
Pair list = (Pair) obj;
if (index == 0)
return list.first();
return this.nth(index - 1, list.rest());
}
public Object valueFromString(String key, String string)
{
Object value = super.valueFromString(key, string);
if (value instanceof Map<?, ?>)
value = this.pairForMap((Map<?, ?>) value);
else if (value instanceof List<?>)
value = this.pairForList((List<?>) value);
return value;
}
private Pair pairForMap(Map<?, ?> map)
{
Pair list = Pair.EMPTY;
for (Object key : map.keySet())
{
Object value = map.get(key.toString());
if (value instanceof Map<?, ?>)
value = this.pairForMap((Map<?, ?>) value);
else if (value instanceof List<?>)
value = this.pairForList((List<?>) value);
list = new Pair(new Pair(key, value), list);
}
return new Pair(new Pair(Symbol.QUOTE, new Pair(list, Pair.EMPTY)), Pair.EMPTY);
}
private Pair pairForList(List<?> list)
{
Pair pairs = Pair.EMPTY;
for (Object value : list)
{
if (value instanceof Map<?, ?>)
value = this.pairForMap((Map<?, ?>) value);
else if (value instanceof List<?>)
value = this.pairForList((List<?>) value);
pairs = new Pair(value, pairs);
}
return new Pair(Symbol.QUOTE, new Pair(pairs, Pair.EMPTY));
}
@ScriptingEngineMethod(language = "All", assetPath = "all_scheduled_scripts.html", category = R.string.docs_script_category_dialogs_notifications, arguments = { })
public Pair scheduledScripts()
{
List<Map<String, String>> scripts = ScheduleManager.allScripts(this._context);
return this.pairForList(scripts);
}
@ScriptingEngineMethod(language = "All", assetPath = "all_namespaces.html", category = R.string.docs_script_category_persistence, arguments = { })
public Pair namespaces()
{
List<String> namespaces = super.namespaceList();
return this.pairForList(namespaces);
}
@ScriptingEngineMethod(language = "All", assetPath = "all_fetch_namespace.html", category = R.string.docs_script_category_persistence, arguments = { "namespaceId" })
public Pair fetchNamespace(String namespace)
{
Map<String, Object> map = super.fetchNamespaceMap(namespace);
return this.pairForMap(map);
}
@ScriptingEngineMethod(language = "All", assetPath = "all_fetch_trigger.html", category = R.string.docs_script_category_triggers_automation, arguments = { "triggerId" })
public Pair fetchTrigger(String id)
{
Map<String, Object> trigger = super.fetchTriggerMap(id);
if (trigger != null)
return this.pairForMap(trigger);
return null;
}
@ScriptingEngineMethod(language = "All", assetPath = "all_fetch_trigger_ids.html", category = R.string.docs_script_category_triggers_automation, arguments = { })
public Pair fetchTriggerIds()
{
List<String> triggerIds = super.fetchTriggerIdList();
return this.pairForList(triggerIds);
}
@ScriptingEngineMethod(language = "All", assetPath = "all_list_tones.html", category = R.string.docs_script_category_dialogs_notifications, arguments = { })
public Pair listTones()
{
List<String> tones = super.fetchToneList();
return (Pair) (((Pair) this.pairForList(tones).getRest()).getFirst());
}
@ScriptingEngineMethod(language = "All", assetPath = "all_fetch_snapshot_ids.html", category = R.string.docs_script_category_data_collection, arguments = { })
public Pair fetchSnapshotIds()
{
List<String> snapshotIds = super.fetchSnapshotIdList();
return (Pair) this.pairForList(snapshotIds).second();
}
@ScriptingEngineMethod(language = "All", assetPath = "all_fetch_snapshot.html", category = R.string.docs_script_category_data_collection, arguments = { "snapshotId" })
public Pair fetchSnapshot(String timestamp)
{
Map<String, Object> snapshot = super.fetchSnapshotMap(timestamp);
if (snapshot != null)
return this.pairForMap(snapshot);
return null;
}
@ScriptingEngineMethod(language = "All", assetPath = "all_probe_config.html", category = R.string.docs_script_category_data_collection, arguments = { "probeName" })
public Pair probeConfig(String name)
{
Map<String, Object> map = this.probeConfigMap(name);
if (map != null)
return this.pairForMap(map);
return null;
}
}